//
// See the file COPYING for redistribution information.
//

#ifndef __NAIPAX_PPQSCHEDULER_HPP__
#define __NAIPAX_PPQSCHEDULER_HPP__

#include <stdint.h>
#include <string>
#include <vector>
#include <Naipax/Ppq.hpp>

namespace Naipax {
    /// A data scheduler utilizing the Ppq module.
    template <typename DispatchClass>
    class PpqScheduler {
    public:
        /** Constructor
         *
         * @param rQueue Persistent queue
         */
        PpqScheduler(Naipax::Ppq<std::string> &rQueue) {
            _mpQueue = &rQueue;
            _mQueueIsCreated = false;
            _mPriorityLevels = _mpQueue->PriorityLevels();
        }

        /** Constructor
         *
         * @param pPathname The pathname for a persistent queue.
         * @param PriorityLevels The number of priorities.
         *        One queue file is created for one priority level.
         * @exception Error::INVALID_ARGUMENT if pPathname is NULL
         *            or PriorityLevels is 0.
         * @exception Error::CANNOT_OPEN_DB if any database of priority
         *            queue cannot be opened.
         * @exception Error::CANNOT_INIT_PPQSCHEDULER If other error occurs.
         */
        PpqScheduler(const char *pPathname, uint32_t PriorityLevels = 2) {
            try {
                _mpQueue = new Ppq<std::string>(pPathname, PriorityLevels);
                _mQueueIsCreated = true;
                _mPriorityLevels = _mpQueue->PriorityLevels();
            }
            catch (std::bad_alloc &E) {
                NAIPAX_THROW(Error::CANNOT_INIT_PPQSCHEDULER);
            }
        }

        /// Destructor
        ~PpqScheduler() {
            if (_mQueueIsCreated == true) {
                delete _mpQueue;
            }
        }

        /** Submit data to scheduler
         *
         * @param rData A single string
         * @exception Error::DATA_OUT_OF_RANGE if the scheduled
         *            priority level exceeds _mPriorityLevels.
         */
        void Feed(const std::string &rData) {
            std::vector<std::string> DataVector;
            DataVector.push_back(rData);
            Feed(DataVector);
        }
        
        /** Submit data to scheduler
         *
         * @param rDataVector A vector of strings
         * @exception Error::DATA_OUT_OF_RANGE if the scheduled
         *            priority level exceeds _mPriorityLevels.
         */
        void Feed(const std::vector<std::string> &rDataVector) {
            for (std::vector<std::string>::const_iterator DataIter
                     = rDataVector.begin();
                 DataIter != rDataVector.end(); ++DataIter) {
                uint32_t Level = _mDispatcher(*DataIter,
                                              _mpQueue->PriorityLevels());
                if (Level >= _mpQueue->PriorityLevels()) {
                    continue;
                }
                _mpQueue->Enqueue((const std::string) *DataIter, Level);
            }
        }

        /// Accessor of _mpQueue
        Naipax::Ppq<std::string>* Queue() { return _mpQueue; }
        
    private:
        DispatchClass _mDispatcher;
        Naipax::Ppq<std::string> *_mpQueue;
        uint32_t _mPriorityLevels;
        bool _mQueueIsCreated;
    };
};

#endif // __NAIPAX_PPQSCHEDULER_HPP__
